package com.wnnight.wys.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.wnnight.wys.entity.StatusCode;
import com.wnnight.wys.exception.ValidateException;
import com.wnnight.wys.filter.ApiAuthenticationFilter;
import com.wnnight.wys.filter.CaptchaFilter;
import com.wnnight.wys.filter.JwtExpireFilter;
import com.wnnight.wys.filter.JwtVerifyFilter;
import com.wnnight.wys.util.repairs.RepairsUtil;
import com.wnnight.wys.vo.ResultVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;


@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OaApiSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtUtils jwtUtils;

    @Bean
    public CaptchaFilter captchaFilter() {
        return new CaptchaFilter();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
//        return NoOpPasswordEncoder.getInstance();
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return (req, resp, exception) -> {
            String r;
            if (exception instanceof UsernameNotFoundException) {
                r = RepairsUtil.objectMapper.writeValueAsString(new ResultVo<>(StatusCode.ACCOUNT_FAIL));
            } else if (exception instanceof BadCredentialsException) {
                r = RepairsUtil.objectMapper.writeValueAsString(new ResultVo<>(StatusCode.ACCOUNT_FAIL));
            } else if (exception instanceof ValidateException) {
                r = RepairsUtil.objectMapper.writeValueAsString(new ResultVo<>(StatusCode.CAPTCHA_FAIL));
            } else {
                r = RepairsUtil.objectMapper.writeValueAsString(new ResultVo<>(StatusCode.ERROR));
            }
            resp.setContentType("application/json;charset=UTF-8");
            resp.getWriter().write(r);
            resp.getWriter().close();
        };
    }


    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return daoAuthenticationProvider;
    }

    @Bean
    public JwtVerifyProvider jwtVerifyProvider() {
        return new JwtVerifyProvider();
    }

    public JwtVerifyFilter jwtVerifyFilter() throws Exception {
        JwtVerifyFilter jwtVerifyFilter = new JwtVerifyFilter("/api/v1/**");
        jwtVerifyFilter.setAuthenticationManager(authenticationManager());
        jwtVerifyFilter.setAuthenticationFailureHandler((req, resp, authtication) -> {
            String r = new ObjectMapper().writeValueAsString(new ResultVo<>(StatusCode.CAPTCHA_FAIL));
            resp.setContentType("application/json;charset=UTF-8");
            resp.getWriter().write(r);
            resp.getWriter().close();
        });
        return jwtVerifyFilter;
    }

    public ApiAuthenticationFilter apiAuthenticationFilter() throws Exception {
        //构造方法的参数是一个URL，只有该URL的请求才会进入该过滤器
        ApiAuthenticationFilter apiAuthenticationFilter = new ApiAuthenticationFilter("/api/v1/wy/login");

        apiAuthenticationFilter.setAuthenticationManager(authenticationManager());

        //认证成功的回调
        apiAuthenticationFilter.setAuthenticationSuccessHandler((req, resp, authentication) -> {
            OaUser user = (OaUser) authentication.getPrincipal();
            String jwt = jwtUtils.creatJWT(user.getId(), user.getUsername(), user.getRoleId());
            resp.addHeader("jwt", jwt);
            String r = new ObjectMapper().writeValueAsString(ResultVo.SUCCESS);
            resp.setContentType("application/json;charset=UTF-8");
            resp.getWriter().write(r);
            resp.getWriter().close();
        });

        apiAuthenticationFilter.setAuthenticationFailureHandler((req, resp, authentication) -> {
            String r = new ObjectMapper().writeValueAsString(new ResultVo<>(StatusCode.ACCOUNT_FAIL));
            resp.setContentType("application/json;charset=UTF-8");
            resp.getWriter().write(r);
            resp.getWriter().close();
        });
        return apiAuthenticationFilter;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(apiAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterAfter(jwtVerifyFilter(), ApiAuthenticationFilter.class);
        http.addFilterAfter(jwtExpireFilter(),JwtVerifyFilter.class);
        http.addFilterBefore(captchaFilter(), ApiAuthenticationFilter.class);
        http.cors().configurationSource(configurationSource());   //跨域配置
        http.antMatcher("/api/v1/**")  //指定以/api/开头的请求使用以下配置
                .authorizeHttpRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()  //请求方法OPTIONS放行
//                .antMatchers("/api/v1/wy/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().csrf().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //将两个Provier增加到Security中
        auth.authenticationProvider(daoAuthenticationProvider()).authenticationProvider(jwtVerifyProvider());
    }

    private CorsConfigurationSource configurationSource() {
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
        corsConfiguration.setAllowedMethods(Arrays.asList("*"));
        corsConfiguration.setExposedHeaders(Arrays.asList("*"));
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/api/**", corsConfiguration);
        return urlBasedCorsConfigurationSource;
    }

    @Bean
    public JwtExpireFilter jwtExpireFilter() {
        return new JwtExpireFilter();
    }

}
